/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package justdoit.controller;

import java.awt.Dimension;
import java.awt.Point;
import justdoit.model.Task;
import justdoit.model.User;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Locale;
import justdoit.model.DefaultSettings;
import justdoit.utility.XMLUtility;

/**
 * Deal with everything about XML file, I/O data
 * @author Muhanad Rahim & Can
 */
public class XMLManager {

    private Document document;
    private List<Element> schemas;
    private final String xmlFileName = "todo.xml";
    /**
     * <justdoit>
     *  <startup>
     *  </startup>
     *  <schema userid="" username="">
     *      <username></username>
     *      <password></password>
     *      <language></language>
     *      <country></country>
     *      <theme></theme>
     *      <tasklist>
     *          <task id="">
     *          ...
     *          </task>
     *      </tasklist>
     *  </schema>
     *  ...
     * </jusdoit>
     */
    private final String rootString = "justdoit";
    private final String schemaString = "schema";
    private final String userIdString = "userid";
    private final String userNameString = "username";
    // user elements
    private final String passwordString = "password";
    private final String languageString = "language";
    private final String countryString = "country";
    private final String themeString = "theme";
    private final String colorRString = "colorr";
    private final String colorGString = "colorg";
    private final String colorBString = "colorb";
    private final String tasklistString = "tasklist";
    // task elements
    private final String taskString = "task";
    private final String taskIdString = "id";
    private final String statusString = "status";
    private final String descriptionString = "description";
    private final String priorityString = "priority";
    private final String duedateString = "duedate";
    private final String categoryString = "category";
    private final String reminderString = "reminder";
    private final String progressString = "progress";
    // startup elements
    private final String defaultSettingsString = "startup";
    private final String defaultUserNameString = "username";
    private final String winXString = "winx";
    private final String winYString = "winy";
    private final String normalWidthString = "winwidth";
    private final String normalHeightString = "winheight";
    private final String compactXString = "compactx";
    private final String compactYString = "compacty";
    private final String compactWidthString = "compactwidth";
    private final String compactHeightString = "compactheight";
    private final String defaultLanguageString = "language";
    private final String defaultCountryString = "country";
    private final String modeString = "mode";

    // Constructor, do some initialization
    public XMLManager() {
        if (loadXMLFile(xmlFileName) < 0) {
            createXMLFile(xmlFileName);
        }
    }

    // only create XML file with root element, no other elements
    private int createXMLFile(String fileName) {
        document = new Document(new Element(rootString));
        return XMLUtility.createXMLFile(document, fileName);
    }

    // load the XML file and load the content to document and schemas
    private int loadXMLFile(String fileName) {
        SAXBuilder parser = new SAXBuilder();
        try {
            document = parser.build(fileName);
            if (document.hasRootElement()) {
                schemas = document.getRootElement().getChildren(schemaString);
                if (schemas == null) {
                    schemas = new ArrayList<Element>();
                }
            }
            return 0;
        } catch (JDOMException e) {
            System.out.println(e.getMessage());
            return -1;
        } catch (IOException e) {
            System.out.println(e.getMessage());
            return -1;
        }
    }

    private int write() {
        return XMLUtility.writeXMLFile(document, xmlFileName);
    }

    // get the name list of all users
    public String[] getUserNameList() {

        if (schemas == null) {
            return null;
        }
        String[] userNameList = new String[schemas.size()];
        for (int i = 0; i < schemas.size(); i++) {
            userNameList[i] = getAttributeValue(schemas.get(i), userNameString);
        }
        return userNameList;
    }

    // return User including all the tasks. null if failed
    public User getUserByUserName(String userName) {
        if (schemas == null) {
            return null;
        }
        Element user = getElementByAttribute(schemas, userNameString, userName);
        if (user == null) {
            return null;
        }
        return generateUserFromElement(user);
    }

    // generate a User object, including all tasks inside it
    private User generateUserFromElement(Element schema) {
        User newUser = new User(
                getAttributeValue(schema, userIdString),
                getAttributeValue(schema, userNameString),
                new Locale(getChildText(schema, languageString),
                getChildText(schema, countryString)));
        newUser.setPasswordHashValue(getChildText(schema, passwordString));
        newUser.setTheme(getChildText(schema, themeString));
        // get tasks
        List<Element> taskElements = schema.getChild(tasklistString).getChildren(taskString);
        for (Element taskElement : taskElements) {
            newUser.getTasks().add(generateTaskFromElement(taskElement));
        }
        return newUser;
    }

    private Task generateTaskFromElement(Element taskElement) {
        Task task = new Task();
        task.setId(taskElement.getAttributeValue(taskIdString));
        task.setCategory(getChildText(taskElement, categoryString));
        task.setPriority(getChildText(taskElement, priorityString));
        task.setProgress(Integer.parseInt(getChildText(taskElement, progressString)));
        task.setStatus(Integer.parseInt(getChildText(taskElement, statusString)));
        task.setDescription(getChildText(taskElement, descriptionString));
        task.setDeadLine(getChildText(taskElement, duedateString));
        task.setReminder(getChildText(taskElement, reminderString));
        return task;
    }

    // return the text of child if it exists or "" if not
    private String getChildText(Element parent, String childName) {
        if (parent.getChild(childName) == null) {
            System.out.println("Can't find child: " + childName);
            return "";
        }
        return parent.getChildText(childName);
    }

    // retur the element which has been set text
    // the "childText" parameter should not be null
    private Element setChildText(Element parent, String childName, String childText) {
        if (parent.getChild(childName) != null) {
            return parent.getChild(childName).setText(childText);
        } else {
            return parent.addContent(new Element(childName).setText(childText));
        }
    }

    // return the value of attribute if it exists or "" if not
    private String getAttributeValue(Element element, String attr) {
        if (element.getAttribute(attr) == null) {
            return "";
        }
        return element.getAttributeValue(attr);
    }

    // return -1 if failed, 0 if success
    // each attribute of task must not be null
    public int addTask(Task newTask, String userId) {
        if (schemas == null) {
            return -1;
        }
        Element user = getElementByAttribute(schemas, userIdString, userId);
        if (user == null) {
            return -1;
        }
        Element tasklist = user.getChild(tasklistString);
        if (tasklist == null) {
            tasklist = new Element(tasklistString);
            user.addContent(tasklist);
        }
        Element taskElement = generateElementFromTask(newTask);
        if (taskElement == null) {
            return -1;
        }
        tasklist.addContent(taskElement);
        return XMLUtility.writeXMLFile(document, xmlFileName);
    }

    // each attribute of task must not be null
    private Element generateElementFromTask(Task task) {

        Element taskElement = new Element(taskString);

        taskElement.setAttribute(taskIdString, task.getId());
        taskElement.addContent(
                new Element(statusString).setText(task.getStatus().toString()));
        taskElement.addContent(
                new Element(descriptionString).setText(task.getDescription()));
        taskElement.addContent(
                new Element(priorityString).setText(task.getPriority()));
        taskElement.addContent(
                new Element(duedateString).setText(task.getDeadLine()));
        taskElement.addContent(
                new Element(categoryString).setText(task.getCategory()));
        taskElement.addContent(
                new Element(reminderString).setText(task.getReminder()));
        taskElement.addContent(
                new Element(progressString).setText(task.getProgress().toString()));

        return taskElement;
    }

    // delete a special task by taskId
    // return 0 if success, -1 if failed
    public int deleteTask(String userId, String taskId) {
        if (schemas == null) {
            return -1;
        }
        Element user = getElementByAttribute(schemas, userIdString, userId);
        if (user == null) {
            return -1;
        }
        List<Element> taskElements =
                user.getChild(tasklistString).getChildren(taskString);
        Element task = getElementByAttribute(taskElements, taskIdString, taskId);
        if (task == null) {
            return -1;
        }
        user.getChild(tasklistString).removeContent(task);
        return write();
    }

    // delete a special task
    public int deleteTask(Task taskToDelete, String userId) {
        return deleteTask(userId, taskToDelete.getId());
    }

    public int updateTask(Task taskToUpdate, String userId) {
        if (schemas == null) {
            return -1;
        }
        Element user = getElementByAttribute(schemas, userIdString, userId);
        if (user == null) {
            return -1;
        }
        List<Element> taskElements = user.getChild(tasklistString).getChildren(taskString);
        Element taskElement = getElementByAttribute(taskElements, taskIdString, taskToUpdate.getId());
        if (taskElement == null) {
            return -1;
        }
        System.out.println("update task: found task!");
        taskElement.setAttribute(taskIdString, taskToUpdate.getId());
        setChildText(taskElement, statusString, taskToUpdate.getStatus().toString());
        setChildText(taskElement, descriptionString, taskToUpdate.getDescription());
        setChildText(taskElement, priorityString, taskToUpdate.getPriority());
        setChildText(taskElement, duedateString, taskToUpdate.getDeadLine());
        setChildText(taskElement, categoryString, taskToUpdate.getCategory());
        setChildText(taskElement, reminderString, taskToUpdate.getReminder());
        setChildText(taskElement, progressString, taskToUpdate.getProgress().toString());
        return write();
    }

    // find a special element by it's attribute value
    // return the element if find, or null if not find
    private Element getElementByAttribute(List<Element> list, String attrName, String attrValue) {
        for (Element l : list) {
            if (getAttributeValue(l, attrName).equals(attrValue)) {
                return l;
            }
        }
        return null;
    }

    // only update schema information, not include tasks
    // return 0 if success, -1 if failed
    public int updateUser(User userToUpdate) {
        Element user = getElementByAttribute(schemas, userIdString, userToUpdate.getId());
        if (user == null) {
            return -1;
        }
        user.setAttribute(userNameString, userToUpdate.getUserName());
        setChildText(user, passwordString, (userToUpdate.getPasswordHashValue()));
        setChildText(user, languageString, userToUpdate.getLocale().getLanguage());
        setChildText(user, countryString, userToUpdate.getLocale().getCountry());
        setChildText(user, themeString, userToUpdate.getTheme());
        return write();
    }

    // return 0 if success, -1 if failed
    public int deleteUser(User userToDelete) {
        Element user = getElementByAttribute(schemas, userIdString, userToDelete.getId());
        if (user == null) {
            return -1;
        }
        document.getRootElement().removeContent(user);
        return write();
    }

    // return 0 if success, -1 if failed
    public int addUser(User newUser) {
        Element schema = new Element(schemaString);
        schema.setAttribute(userIdString, newUser.getId());
        schema.setAttribute(userNameString, newUser.getUserName());
        schema.addContent(new Element(passwordString).setText(newUser.getPasswordHashValue()));
        schema.addContent(new Element(languageString).setText(newUser.getLocale().getLanguage()));
        schema.addContent(new Element(countryString).setText(newUser.getLocale().getCountry()));
        schema.addContent(new Element(tasklistString));
        document.getRootElement().addContent(schema);
        return write();
    }

    public DefaultSettings getDefaultSettings() {
        DefaultSettings ds = new DefaultSettings();
        Element dsElement = document.getRootElement().getChild(defaultSettingsString);
        if (dsElement != null) {
            try {
                ds.setUserName(getChildText(dsElement, defaultUserNameString));
                ds.setLocale(new Locale(getChildText(dsElement, defaultLanguageString),
                        getChildText(dsElement, defaultCountryString)));
                ds.setNormalHeight(Integer.parseInt(getChildText(dsElement, normalHeightString)));
                ds.setNormalWidth(Integer.parseInt(getChildText(dsElement, normalWidthString)));
                ds.setCompactDimension(new Dimension(
                        Integer.parseInt(getChildText(dsElement, compactWidthString)),
                        Integer.parseInt(getChildText(dsElement, compactHeightString))));
                ds.setCompactLocation(new Point(
                        Integer.parseInt(getChildText(dsElement, compactXString)),
                        Integer.parseInt(getChildText(dsElement, compactYString))));
                ds.setWinX(Integer.parseInt(getChildText(dsElement, winXString)));
                ds.setWinY(Integer.parseInt(getChildText(dsElement, winYString)));
                ds.setMode(getChildText(dsElement, modeString));

            } catch (NumberFormatException e) {
                System.out.println(e.getLocalizedMessage());
            }
        }
        return ds;
    }

    public int updateDefaultSettings(DefaultSettings ds) {
        Element dsElement = document.getRootElement().getChild(defaultSettingsString);
        if (dsElement == null) {
            dsElement = new Element(defaultSettingsString);
            document.getRootElement().addContent(dsElement);
        }
        if (dsElement != null) {
            setChildText(dsElement, defaultUserNameString, ds.getUserName());
            setChildText(dsElement, defaultLanguageString, ds.getLocal().getLanguage());
            setChildText(dsElement, defaultCountryString, ds.getLocal().getCountry());
            setChildText(dsElement, winXString, ds.getWinX().toString());
            setChildText(dsElement, winYString, ds.getWinY().toString());
            if (ds.getCompactDimension() != null) {
                setChildText(dsElement, compactWidthString, String.valueOf(ds.getCompactDimension().width));
                setChildText(dsElement, compactHeightString, String.valueOf(ds.getCompactDimension().height));
            }
            if (ds.getCompactLocation() != null) {
                setChildText(dsElement, compactXString, String.valueOf(ds.getCompactLocation().x));
                setChildText(dsElement, compactYString, String.valueOf(ds.getCompactLocation().y));
            }
            setChildText(dsElement, normalWidthString, ds.getNormalWidth().toString());
            setChildText(dsElement, normalHeightString, ds.getNormalHeight().toString());
            setChildText(dsElement, modeString, ds.getMode());
            return write();
        }
        return -1;
    }

    public List<String> getAllCategories(String userName) {
        if (schemas == null) {
            return null;
        }
        Element user = getElementByAttribute(schemas, userNameString, userName);
        if (user == null) {
            return null;
        }
        Element tasklist = user.getChild(tasklistString);
        if (tasklist == null) {
            return null;
        }
        List<Element> tasks = tasklist.getChildren(taskString);
        List<String> categories = new ArrayList<String>();
        for (Element task : tasks) {
            String category = getChildText(task, categoryString);
            if (category != null && category != "" && !categories.contains(category)) {
                categories.add(category);
            }
        }
        return categories;
    }

    public static void main(String[] args) throws IOException {
    }
}
